Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

284
Vistas
Java generics: Does "T2 extends T1" imply "T1 super T2"?

If I have a generic <T2 extends T1>, does the compiler infer T1 super T2?

I have a more complex collection, which I reduced to the MWE below. The collection shall be mergeable with any such collection with elements of any subtype.

Now I'm wondering, why the call to forEach in merge is not accepted. It fails with

java.util.function.Consumer<java.util.Optional> cannot be converted to java.util.function.Consumer<java.util.Optional<? super T2>>

I've depicted the type relationships in the diagram below. In merge, T2 extends T1. The call to forEach is applied on the other object, hence the T1 of this becomes the ? of other.forEach and the T2 of this.merge is the T1 of other. Hence, this' T1 should be accepted as super of other's T1.

I also tried public void merge(Group<? extends T> other) with the same result. And public void merge(Group<T> other) does not accept such collections with elements of any subtype of T1.

MWE:

class Group<T1> {
    public <T2 extends T1> void merge(Group<T2> other) {
        Consumer<Optional<T1>> task = t -> t.ifPresentOrElse(this::foo, this::bar);
        other.forEach(task);
    }

    public Collection<Optional<T1>> values() {
        return List.of();
    }

    public void forEach(Consumer<Optional<? super T1>> consumer) {
        values().forEach(consumer);
    }

    private void foo(T1 t) {}

    private void bar() {}
}

Relationships:

   this.T1      -becomes->  other.forEach.?
      ^                            |
      |                          super
   extends                         |
      |                            v
this.merge.T2     -is->        other.T1
over 4 years ago · Santiago Trujillo
2 Respuestas
Responde la pregunta

0

If you have a Consumer<Optional<? super T1>> consumer then you can pass it (to the accept(T) method) an Optional<Object> because Object is a supertype of T1.

On the other hand, you cannot pass an Optional<Object> to Consumer<Optional<T1>> task because T1 might not be Object.

So you cannot assign the Consumer<Optional<T1>> task to the Consumer<Optional<? super T1>> consumer to prevent your consumer from being passed objects of types it does not expect.

over 4 years ago · Santiago Trujillo Denunciar

0

You can solve your problem using

class Group<T1> {
    public <T2 extends T1> void merge(Group<T2> other) {
        Consumer<Optional<? extends T1>> task=t->t.ifPresentOrElse(this::foo, this::bar);
        other.forEach(task);
    }

    public Collection<Optional<T1>> values() {
        return List.of();
    }

    public void forEach(Consumer<? super Optional<T1>> consumer) {
        values().forEach(consumer);
    }

    private void foo(T1 t) {}

    private void bar() {}
}

This is an application of the PECS rule.

Your task will treat the Optional as a producer, hence, has to declare Optional<? extends T1> to allow the optional to produce subtypes of T1. We know that optional will always act as a producer, but Java’s generic type system does not have the concept of classes always acting like a producer or consumer and needs an explicit ? extends T1 here.

The forEach receives a consumer, even literally in the interface name, so ? super … is the right way to declare that this consumer may be a consumer of supertypes of Optional<T1>. This does not only include Object, the superclass of Optional, but also Optional<? extends T1> when T1 := T2, as in the invocation within merge, as Optional<? extends T1> is a supertype of Optional<T2>.


Of course, you could also solve the issue by simply using

Consumer<Optional<T2>> task = t -> t.ifPresentOrElse(this::foo, this::bar);

as this only requires foo to accept a T2 argument, which it always does, as T2 is a subtype of T1. But changing the signature of forEach to raise the flexibility is always a good move. Since it’s a public method, there might be other callers benefitting from it.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda